Skip to content

Code cleanup, Refactor, and consistency fixes#52

Merged
phantomcortex merged 13 commits into
mainfrom
codec-stack-remediation
Jun 10, 2026
Merged

Code cleanup, Refactor, and consistency fixes#52
phantomcortex merged 13 commits into
mainfrom
codec-stack-remediation

Conversation

@phantomcortex

Copy link
Copy Markdown
Owner

No description provided.

phantomcortex and others added 13 commits June 8, 2026 10:19
The force-install OCI artifact snapshotted libavcodec-freeworld,
gstreamer1-plugins-bad-freeworld, mesa-*-freeworld, and vvenc-libs
on a schedule independent of the main image. Those RPMs link against
versioned x265/vvenc symbols (e.g. x265_api_get_215, libvvenc.so.1.13)
that no longer exist on the rolling Bazzite base, producing runtime
errors when ffmpeg or GStreamer try to load them.

Install the freeworld codec stack atomically in 02-build.sh via a
narrowly-scoped dnf swap allowlist instead — versions co-resolve
against the live repos, so ABI skew is impossible by construction.
Adds a codec-integrity stage to 08-validate.sh (ldd + ffmpeg
-encoders + gst-inspect) that turns this class of failure into a
build-time hard fail instead of a runtime one.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three issues exposed by the first testing build:

1. gstreamer1-plugins-bad-{free,freeworld} are NOT exclusive — bad-free
   ships libgstva.so (the VAAPI plugin); freeworld only adds x265enc,
   de265, and rtmp on top. The swap was silently removing the va plugin,
   breaking GStreamer HW codec acceleration. Switch to an additive install.

2. The Tier 1/2/3 encoder regex required five literal dots in the
   capability field, but real ffmpeg output is "V....D" (four dots + D
   for direct-rendering support). Every encoder check was a false
   negative. Use "[.A-Z]{5}" to match any capability flag combination.

3. libavcodec-freeworld is a parallel-library package useful only when
   the system runs Fedora's ffmpeg-free. Bazzite ships RPMFusion's full
   ffmpeg which bundles patent codecs in its own libavcodec, so the
   freeworld variant is uninstallable (file conflict) and unnecessary.
   Drop it from CRITICAL_PACKAGES/VERSIONLOCK_PACKAGES; the swap stays
   as a best-effort no-op for non-Bazzite bases.

Also: stop suppressing dnf stderr in the codec section — failures now
log dnf's error message inline for diagnostics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Second testing build surfaced two new failure modes:

1. F44 RPMFusion's i686 builds of libavcodec-freeworld (8.0.1-6) and
   gstreamer1-plugins-bad-freeworld (1.28.1-3) are stale — they require
   libx265.so.215 which the current x265 no longer provides (now .216).
   The x86_64 builds are fresh and link correctly. Bazzite pulls i686 by
   default for Steam multilib, so a bare install pulls both arches and
   aborts on the stale i686. Pin both to .x86_64 — no 32-bit userland
   needs patent codec libraries.

2. vvenc-libs is a transitive dep of ffmpeg and was already installed by
   the time we reached the explicit `dnf install vvenc vvenc-libs
   svt-av1-libs` line. dnf5 aborts the entire transaction with "Failed to
   resolve" when any requested package is a no-op, even if others would
   install successfully. Pre-filter against `rpm -q` so we only ask dnf
   for packages it can actually install.

The dnf_codec_swap function now accepts an optional arch suffix that only
applies to its direct-install fallback path; the swap path itself remains
arch-agnostic since it inherits whatever arches the source had.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
00-kernel.sh: remove `dnf5 versionlock add kernel-cachyos-lto` — no-op
on ostree (already documented in 02-build.sh).

07-config.sh: drop the global `set +eu` flip-without-restore; switch the
two `((counter++))` increments to arithmetic assign so the script keeps
`set -euo pipefail` end-to-end. Also drops the unused "hide incompatible
Bazzite recipes" section.

08-validate.sh: remove the broken "Experimential" ldconfig stanza
(truncated path, `ldconfig -i` misuse), plus the GDK pixbuf /
GLib schemas / font registration / x265enc checks that overlapped with
02-build.sh's own validation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Delete build_files/wine-installer.sh (~280 lines, no caller in the
  Containerfile RUN chain or anywhere else in the repo).
- Drop counter_init / counter_increment from 95-utility-functions.sh
  (no call sites). counter_display is retained for 02-build.sh:280.
- Replace README.md with a merge of the option-a + option-c drafts,
  dropping the CachyOS kernel and freeworld Mesa feature copy and the
  stale Build Architecture script tree. Removes README-option-{a,b,c}.md.
- Delete docs/claude.md (legacy AI-context doc; CLAUDE.md + .claude/
  skills/ now cover this role) and drop its README link.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 00-kernel.sh: shebang `/bin/bash` → `/usr/bin/bash`, quote $pkg in
  the rpm-erase loop (shellcheck SC2086).
- 06-fix-opt.sh: replace raw `echo` status lines with `log_info`
  per the shell-conventions skill.
- 07-config.sh: collapse the five-line `echo` summary block to a
  single `log_success` (the log_section banners above already trace
  what ran).
- Justfile: `Virtal` → `Virtual` in the ten `[group(...)]` labels.
- docs/developer.md: drop the wine-installer.sh entry from the
  directory layout to match the deletion in the prior commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build_files/02-build.sh + build_files/manifests/*.list
  Package lists move out of the script into eight declarative
  `.list` manifests (fedora, fedora-multimedia, rpmfusion, terra,
  brave, critical, remove, plus a `coprs.list`). 02-build.sh now
  reads them via `read_manifest`, captures dnf output to a log file
  (`dnf_log_tail` on failure), and authenticates GitHub API calls
  through a BuildKit secret mount via the new `github_api_get`
  helper. The standalone `--allowerasing` guard wrappers were
  consolidated into 95-utility-functions.sh (prior commit).

.github/workflows/build.yml
  Drop `system_files/**` from `paths-ignore` — those files are
  COPY'd into the image, so ignoring them silently shipped stale
  configs until the next scheduled rebuild. Also mounts
  `GITHUB_TOKEN` as a buildah `--secret` so build scripts can hit
  the GitHub API without sharing the anonymous runner-pool rate
  limit. Secret file is created with mode 0600 and removed
  post-build.

system_files/etc/sudoers.d/99-distinction-wheel-nopasswd
  Replace blanket `%wheel NOPASSWD: ALL` with a scoped policy:
  passwordless for the atomic-system commands used daily
  (`rpm-ostree`, `bootc`, `ostree`, `systemctl`, `flatpak`), plus
  a 60-minute credential cache for everything else. Old Option-A
  behaviour preserved in comments for easy revert.

CLAUDE.md
  Project-level Claude Code context: working agreement, commands,
  hard rules (no `--allowerasing`, manifests are the source of
  truth for package data, `dnf versionlock add` is a no-op on
  ostree and must not return).

.claude/skills/
  Three skill files loaded automatically by Claude Code:
  `build-pipeline` (Containerfile order, mount gotchas, manifest
  layout), `shell-conventions` (logging library, error-handling
  policy per script), `keep-it-simple` (minimal-diff guardrails).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- distinction-functions.sh: the CYAN constant was \033[031m, which is
  red, not cyan. Switch to \033[36m so the variable name matches what
  the terminal renders (used in the rm-confirm prompt and the `naut`
  helper).
- 05-remote-grabber.sh: shebang `/bin/bash` → `/usr/bin/bash`, the last
  hold-out among build_files/*.sh.
- Rename .github/workflows/clean-images.yaml → .yml so all workflow
  files share one extension.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build-cache.yml and build-force-install.yml were ~95% identical:
same checkout/login, same skopeo inspect, same version-diff decision
logic, same buildx push. Move the shared body into a new
_build-oci-artifact.yml that exposes `image`, `builder_dir`,
`version_check_tag`, `max_age_days`, and `force_rebuild` as
workflow_call inputs.

The callers shrink to ~30 lines apiece — they keep their own cron,
workflow_dispatch, and workflow_call triggers (preserving any
external cross-repo callers), set permissions, and forward inputs to
the reusable workflow with `secrets: inherit`.

Other changes folded in:

- Action versions in the reusable workflow are SHA-pinned with `# vN`
  annotations, matching build.yml's hardening style (build-cache /
  build-force-install previously used floating `@v6` / `@v4` / `@v7`
  tags, so dependabot can now pin minor bumps the same way it does
  for build.yml).
- Documented the 15-day cadence on build-cache.yml: build.yml and
  build-force-install.yml run every 10 days; cache runs every 15
  because its upstream sources rarely change, and the workflow's own
  inspect→decide step is the real freshness gate (cron is just the
  heartbeat).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- build-disk.yml: switch BIB_IMAGE from the lorbuschris fork pin
  (`:20250608`) back to upstream `quay.io/centos-bootc/bootc-image-builder:latest`.
  osbuild#954 — the reason for the fork — merged 2025-06-13.

- distinction-functions.sh `mkcd()`: collapse the three-branch
  case "$SHELL_TYPE" to a single printf. SHELL_TYPE was never set
  anywhere, so the zsh and bash branches were unreachable; the printf
  fallback produces the same output and works in either shell.
  (Not addressed here: the file still uses zsh-only `read -q` in rm()
  while declaring `#!/bin/sh` — separate sweep.)

- distinctionos-steam-linker.service: replace the placeholder
  `Documentation=https://github.com/yourusername/DistinctionOS` with
  the real repo URL.

- Nautilus: delete the 320-line org.gnome.Nautilus.desktop override
  (mostly a stale translation snapshot drifting against upstream) and
  move the single real customisation — `Name=Files` → `Name=Nautilus`
  in the C locale — into 07-config.sh as a sed step, mirroring the
  existing Cider / Winetricks patches. Localised Name[xx]= entries
  are deliberately left alone, matching the prior override.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The file is sourced from /etc/profile (bash login) and /etc/zsh/zshrc
(every zsh interactive shell), so it has to work in both. The shebang
claimed `#!/bin/sh` but the body has always used `[[ ]]`, `local`, `=~`,
and `disown` — none POSIX.

The actual runtime break was rm(): it used zsh-only `read -q "REPLY?"`
to read a single keystroke into REPLY. Under bash, `read -q` is not a
real flag, so the prompt-and-confirm path errored out.

- Shebang: `/bin/sh ` (trailing space) → `/usr/bin/bash`. Adds a header
  comment naming the two sourcing paths and noting the bash/zsh tolerance
  is intentional.
- rm(): switch to plain `read -r REPLY` (press Enter). Loses the
  single-keystroke convenience zsh got from `read -q`, but the rest of
  the confirm logic (`[[ "$REPLY" =~ ^[Yy]$ ]]`) keeps the same semantics
  and now works in both shells. Drops the now-unneeded trailing `echo`
  since the user already pressed Enter.

Verified by sourcing the file under `bash -c` and `zsh -c`, listing the
five defined functions, and running rm() on a fixture directory with
both `n` (kept) and `y` (deleted) input. shellcheck -S error clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@phantomcortex
phantomcortex marked this pull request as ready for review June 10, 2026 22:16
@phantomcortex
phantomcortex merged commit bca5e99 into main Jun 10, 2026
3 of 4 checks passed
@phantomcortex
phantomcortex deleted the codec-stack-remediation branch June 10, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant